home *** CD-ROM | disk | FTP | other *** search
- /* {{{ Copyright */
-
- /* File managing
- Copyright (C) 1994, 1995 The Free Software Foundation
-
- Written by: 1994, 1995 Janne Kukonlehto
- 1994, 1995 Fred Leeflang
- 1994, 1995 Miguel de Icaza
- 1995 Jakub Jelinek
-
- The copy code was based in GNU's cp, and was written by:
- Torbjorn Granlund, David MacKenzie, and Jim Meyering.
-
- The move code was based in GNU's mv, and was written by:
- Mike Parker and David MacKenzie.
-
- Janne Kukonlehto added much error recovery to them for being used
- in an interactive program.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /* }}} */
-
- #include <config.h>
- /* Hack: the vfs code should not rely on this */
- #define WITH_FULL_PATHS 1
-
- /* {{{ Include files */
-
- #include <sys/types.h>
- #include <dirent.h>
- #include <stdio.h>
- #include <errno.h>
- #include "tty.h"
- #include <ctype.h>
- #include <malloc.h>
- #include <string.h>
- #ifdef HAVE_UNISTD_H
- # include <unistd.h>
- #endif
- #include <sys/stat.h>
- #include <sys/param.h>
- #include <fcntl.h>
- #include <time.h>
- #ifdef _OS_NT
- #include <sys/utime.h>
- #else
- #include <utime.h>
- #endif
- #include "mad.h"
- #include "regex.h"
- #include "util.h"
- #include "dialog.h"
- #include "global.h"
- /* Needed by query_replace */
- #include "color.h"
- #include "win.h"
- #include "dlg.h"
- #include "widget.h"
- #define WANT_WIDGETS
- #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
- #include "file.h"
- #include "layout.h"
- #include "widget.h"
- #include "wtools.h"
-
- /* This for the background code, we currenlty only support one
- * way of comunicating the background and foreground process
- * by using the socketpair system call
- */
- #ifdef USE_NETCODE
- # include <sys/socket.h>
- #endif
-
- /* Needed for current_panel, other_panel and WTree */
- #include "dir.h"
- #include "panel.h"
- #include "tree.h"
-
- #include "key.h"
- #include "../vfs/vfs.h"
-
- /* }}} */
-
- /* rcsid [] = "$Id: file.c,v 1.12 1995/02/21 19:05:47 miguel Exp $" */
- int verbose = 1;
-
- /* This is a hard link cache */
- struct link {
- struct link *next;
- vfs *vfs;
- dev_t dev;
- ino_t ino;
- short linkcount;
- char name[1];
- };
-
- struct link *linklist = NULL;
- struct re_pattern_buffer rx;
- struct re_registers regs;
- static char *dest_mask = NULL;
- static int op_follow_symlinks = 0;
- int dive_into_subdirs = 1;
- int stable_symlinks = 0;
- int preserve_uidgid = 1;
-
- #define WX 60
- #define WY 10
- #define BY 10
-
- static Dlg_head *op_dlg;
- static char *op_names [] = { " Copy ", " Move ", " Delete " };
- static int selected_button;
- static int last_percentage [3];
-
- enum { REPLACE_YES = B_USER, REPLACE_NO, REPLACE_APPEND, REPLACE_ALWAYS,
- REPLACE_UPDATE, REPLACE_NEVER, REPLACE_ABORT };
-
- static int replace_colors [4];
- static Dlg_head *replace_dlg;
- static char *replace_filename;
- static struct stat *s_stat, *d_stat;
- static int replace_result;
-
- int know_what_am_i_doing = 1;
-
- enum { RECURSIVE_YES, RECURSIVE_NO, RECURSIVE_ALWAYS,
- RECURSIVE_NEVER, RECURSIVE_ABORT };
-
- static int recursive_result;
- static int recursive_erase (char *s);
-
- static WLabel *FileLabel [2];
- static WLabel *FileString [2];
- static WLabel *ProgressLabel [3];
- static WGauge *ProgressGauge [3];
-
- /* }}} */
-
- /* {{{ File progress display routines */
-
- static void update_buttons (void)
- {
- #ifndef HAVE_X
- #define h op_dlg
- int minus = verbose ? 0 : 3;
-
- attrset ((selected_button == FILE_SKIP) ? FOCUSC : NORMALC);
- dlg_move (op_dlg, BY-minus, 14);
- addstr ("[ Skip ]");
- attrset ((selected_button == FILE_ABORT) ? FOCUSC : NORMALC);
- dlg_move (op_dlg, BY-minus, WX - 19);
- addstr ("[ Abort ]");
- refresh ();
- #undef h
- #endif
- }
-
- static int check_buttons (void)
- {
- #ifndef HAVE_X
- int c;
-
- #if 0
- rotate_dash ();
- #endif
-
- /* no delay please says the 1 there */
- c = get_key_code (1);
-
- if (c == ERR)
- return FILE_CONT;
-
- switch (c){
- case 's':
- selected_button = FILE_SKIP;
- update_buttons ();
- return selected_button;
- case 'a':
- case KEY_F(10):
- case XCTRL('g'):
- case XCTRL('c'):
- case ESC_CHAR:
- selected_button = FILE_ABORT;
- update_buttons ();
- return selected_button;
- case ' ':
- case '\n':
- case KEY_ENTER:
- return selected_button;
- case '\t':
- case KEY_LEFT:
- case KEY_RIGHT:
- selected_button = (selected_button == FILE_SKIP) ? FILE_ABORT : FILE_SKIP;
- update_buttons ();
- /* Fall through */
- default:
- return FILE_CONT;
- }
- #else
- #ifdef HAVE_TK
- tk_dispatch_all ();
- #endif
- #ifdef HAVE_XVIEW
- xv_dispatch_something ();
- #endif
- if (op_dlg->running)
- return FILE_CONT;
- # ifdef ZERO
- /*FIXME: I think FRAME_CMD kills the dialog whenever we click a button, so that is
- why this is ifdefed*/
- else if (op_dlg->ret_value == B_CANCEL)
- return FILE_ABORT;
- else
- return op_dlg->ret_value;
- # else
- return FILE_ABORT;
- # endif
- #endif
- }
-
- static int op_win_callback (struct Dlg_head *h, int id, int msg)
- {
- switch (msg){
- #ifndef HAVE_X
- case DLG_DRAW:
- attrset (REVERSE_COLOR);
- dlg_erase (h);
- draw_box (h, 1, 2, h->lines-2, h->cols-4);
- update_buttons ();
- return 1;
- #endif
- }
- return 0;
- }
-
- /* Used to save the hint line */
- static int last_hint_line;
-
- void create_op_win (int op)
- {
- int i;
- #ifdef HAVE_XVIEW
- char *sixty = " ";
- char *fifteen = " ";
- #else
- char *sixty = "";
- char *fifteen = "";
- #endif
-
- replace_result = 0;
- recursive_result = 0;
-
- op_dlg = create_dlg (0, 0, WY+4, WX+4, dialog_colors,
- op_win_callback, "", "opwin", DLG_CENTER);
-
- last_hint_line = the_hint->widget.y;
- if ((op_dlg->y + op_dlg->lines) > last_hint_line)
- the_hint->widget.y = op_dlg->y + op_dlg->lines+1;
-
- x_set_dialog_title (op_dlg, "");
-
- #ifndef HAVE_X
- add_widget (op_dlg, button_new (0, 0, 0, "", 0, -1, 0, 0));
- #else
- tk_new_frame (op_dlg, "b.");
- add_widgetl (op_dlg, button_new (0, 0, FILE_ABORT, "Abort", 0, -1, 0, 0),
- XV_WLAY_RIGHTOF);
- add_widgetl (op_dlg, button_new (0, 0, FILE_SKIP, "Skip", 0, -1, 0, 0),
- XV_WLAY_CENTERROW);
- #endif
- tk_new_frame (op_dlg, "2.");
- add_widgetl (op_dlg, ProgressGauge [2] = gauge_new (8, 14, 0, 100, 0),
- XV_WLAY_RIGHTOF);
- add_widgetl (op_dlg, ProgressLabel [2] = label_new (8, 5, fifteen),
- XV_WLAY_NEXTROW);
- tk_new_frame (op_dlg, "1.");
- add_widgetl (op_dlg, ProgressGauge [1] = gauge_new (7, 14, 0, 100, 0),
- XV_WLAY_RIGHTOF);
- add_widgetl (op_dlg, ProgressLabel [1] = label_new (7, 5, fifteen),
- XV_WLAY_NEXTROW);
- tk_new_frame (op_dlg, "0.");
- add_widgetl (op_dlg, ProgressGauge [0] = gauge_new (6, 14, 0, 100, 0),
- XV_WLAY_RIGHTOF);
- add_widgetl (op_dlg, ProgressLabel [0] = label_new (6, 5, fifteen),
- XV_WLAY_NEXTROW);
- tk_new_frame (op_dlg, "f1.");
- add_widgetl (op_dlg, FileString [1] = label_new (4, 14, sixty),
- XV_WLAY_RIGHTOF);
- add_widgetl (op_dlg, FileLabel [1] = label_new (4, 5, fifteen),
- XV_WLAY_NEXTROW);
- tk_new_frame (op_dlg, "f0.");
- add_widgetl (op_dlg, FileString [0] = label_new (3, 14, sixty),
- XV_WLAY_RIGHTOF);
- add_widgetl (op_dlg, FileLabel [0] = label_new (3, 5, fifteen),
- XV_WLAY_NEXTROW);
-
- /* We will manage the dialog without any help, that's why
- we have to call init_dlg */
- init_dlg (op_dlg);
- op_dlg->running = 1;
- selected_button = FILE_SKIP;
- update_buttons ();
- for (i = 0; i < 3; i++)
- last_percentage [i] = -99;
- }
-
- void destroy_op_win (void)
- {
- #ifdef HAVE_XVIEW
- xtoolkit_kill_dialog (op_dlg);
- #endif
- dlg_run_done (op_dlg);
- destroy_dlg (op_dlg);
- the_hint->widget.y = last_hint_line;
- }
-
- static int show_no_bar (int n)
- {
- if (n >= 0) {
- label_set_text (ProgressLabel [n], "");
- gauge_show (ProgressGauge [n], 0);
- }
- return check_buttons ();
- }
-
- #ifndef HAVE_X
- #define truncFileString(s) name_trunc (s, 47)
- #else
- #define truncFileString(s) s
- #endif
-
- static int show_source (char *s)
- {
- if (s != NULL){
-
- #ifdef WITH_FULL_PATHS
- int i = strlen (cpanel->cwd);
-
- /* We remove the full path we have added before */
- if (!strncmp (s, cpanel->cwd, i)){
- if (s[i] == PATH_SEP)
- s += i + 1;
- }
- #endif /* WITH_FULL_PATHS */
-
- label_set_text (FileLabel [0], "Source");
- label_set_text (FileString [0], truncFileString (s));
- return check_buttons ();
- } else {
- label_set_text (FileLabel [0], "");
- label_set_text (FileString [0], "");
- return check_buttons ();
- }
- }
-
- static int show_target (char *s)
- {
- if (s != NULL){
- label_set_text (FileLabel [1], "Target");
- label_set_text (FileString [1], truncFileString (s));
- return check_buttons ();
- } else {
- label_set_text (FileLabel [1], "");
- label_set_text (FileString [1], "");
- return check_buttons ();
- }
- }
-
- static int show_deleting (char *s)
- {
- label_set_text (FileLabel [0], "Deleting");
- label_set_text (FileString [0], truncFileString (s));
- return check_buttons ();
- }
-
- static int show_bar (int n, long done, long total)
- {
- gauge_set_value (ProgressGauge [n], (int) total, (int) done);
- gauge_show (ProgressGauge [n], 1);
- return check_buttons ();
- }
-
- static int show_file_progress (long done, long total)
- {
- if (!verbose)
- return check_buttons ();
- if (total > 0){
- label_set_text (ProgressLabel [0], "File");
- return show_bar (0, done, total);
- } else
- return show_no_bar (0);
- }
-
- static int show_count_progress (long done, long total)
- {
- if (!verbose)
- return check_buttons ();
- if (total > 0){
- label_set_text (ProgressLabel [1], "Count");
- return show_bar (1, done, total);
- } else
- return show_no_bar (1);
- }
-
- static int show_bytes_progress (long done, long total)
- {
- if (!verbose)
- return check_buttons ();
- if (total > 0){
- label_set_text (ProgressLabel [2], "Bytes");
- return show_bar (2, done, total);
- } else
- return show_no_bar (2);
- }
-
- /* }}} */
-
- /* {{{ Query/status report routines */
-
- static int do_file_error (char *error)
- {
- int result;
-
- result = query_dialog (" Error ", error, 3, 3, " Skip ", " Retry ", " Abort ");
-
- switch (result){
- case 0:
- do_refresh ();
- return FILE_SKIP;
- case 1:
- do_refresh ();
- return FILE_RETRY;
- case 2:
- default:
- return FILE_ABORT;
- }
- }
-
- /* Report error with one file */
- static int file_error (char *format, char *file)
- {
- sprintf (cmd_buf, format,
- name_trunc (file, 30), unix_error_string (errno));
- return do_file_error (cmd_buf);
- }
-
- /* Report error with two files */
- static int files_error (char *format, char *file1, char *file2)
- {
- sprintf (cmd_buf, format, name_trunc (file1, 15),
- name_trunc (file2, 15), unix_error_string (errno));
- return do_file_error (cmd_buf);
- }
-
- static char *format = "Target file \"%s\" already exists!";
- static int replace_callback (struct Dlg_head *h, int Id, int Msg)
- {
- #ifndef HAVE_X
-
- switch (Msg){
- case DLG_DRAW:
- attrset (ERROR_COLOR);
- dlg_erase (h);
- draw_box (h, 1, 2, 12, 56);
-
- dlg_move (h, 1, 24);
- addstr (" File exists ");
- break;
- }
- #endif
- return 0;
- }
-
- static void init_replace (void)
- {
- char buffer [128];
-
- replace_colors [0] = ERROR_COLOR;
- replace_colors [1] = REVERSE_COLOR;
- replace_colors [2] = ERROR_COLOR;
- replace_colors [3] = REVERSE_COLOR;
-
- replace_dlg = create_dlg (0, 0, 14, 60, replace_colors, replace_callback,
- "[Replace]", "replace", DLG_CENTER);
-
- x_set_dialog_title (replace_dlg, "File exists");
-
- #ifdef HAVE_X
- #define X_TRUNC 128
- #else
- #define X_TRUNC 52
- #endif
- sprintf (buffer, format, name_trunc (replace_filename, X_TRUNC - strlen (format)));
- add_widgetl (replace_dlg, label_new (3, 5, buffer), XV_WLAY_CENTERROW);
-
- add_widgetl (replace_dlg,
- button_new (BY + 1, 46, REPLACE_ABORT, "[ Abort ]", 'a', 2, 0, 0),
- XV_WLAY_CENTERROW);
-
- tk_new_frame (replace_dlg, "a.");
- add_widgetl (replace_dlg,
- button_new (BY - 1, 47, REPLACE_NEVER, "[ nonE ]", 'e', 5, 0, 0),
- XV_WLAY_RIGHTOF);
- add_widgetl (replace_dlg,
- button_new (BY - 1, 36, REPLACE_UPDATE, "[ Update ]", 'u', 2, 0, 0),
- XV_WLAY_RIGHTOF);
- add_widgetl (replace_dlg,
- button_new (BY - 1, 28, REPLACE_ALWAYS, "[ alL ]", 'l', 4, 0, 0),
- XV_WLAY_RIGHTOF);
-
- add_widgetl (replace_dlg, label_new (BY-1, 5, "Overwrite all targets?"),
- XV_WLAY_CENTERROW);
-
- /* "this target..." widgets */
- tk_new_frame (replace_dlg, "p.");
- if (!S_ISDIR (d_stat->st_mode))
- add_widgetl (replace_dlg,
- button_new (BY - 2, 43, REPLACE_APPEND, "[ apPend ]", 'p', 2, 0, 0),
- XV_WLAY_RIGHTOF);
- add_widgetl (replace_dlg,
- button_new (BY - 2, 36, REPLACE_NO, "[ No ]", 'n', 2, 0, 0),
- XV_WLAY_RIGHTOF);
- add_widgetl (replace_dlg,
- button_new (BY - 2, 28, REPLACE_YES, "[ Yes ]", 'y', 2, 0, 0),
- XV_WLAY_RIGHTOF);
- add_widgetl (replace_dlg, label_new (BY-2,5, "Overwrite this target?"),
- XV_WLAY_CENTERROW);
-
- tk_new_frame (replace_dlg, "i.");
- sprintf (buffer, "Target date: %s, size %d",
- file_date (d_stat->st_mtime), (int) d_stat->st_size);
- add_widgetl (replace_dlg, label_new (6, 5, buffer), XV_WLAY_CENTERROW);
- sprintf (buffer, "Source date: %s, size %d",
- file_date (s_stat->st_mtime), (int) s_stat->st_size);
- add_widgetl (replace_dlg, label_new (5, 5, buffer), XV_WLAY_CENTERROW);
- tk_end_frame ();
- }
-
- static int do_append = 0;
-
- static int query_replace (char *destname, struct stat *_s_stat,
- struct stat *_d_stat)
- {
- if (replace_result < REPLACE_ALWAYS){
- replace_filename = destname;
- s_stat = _s_stat;
- d_stat = _d_stat;
- init_replace ();
- run_dlg (replace_dlg);
- replace_result = replace_dlg->ret_value;
- if (replace_result == B_CANCEL)
- replace_result = REPLACE_ABORT;
- destroy_dlg (replace_dlg);
- }
-
- switch (replace_result){
- case REPLACE_UPDATE:
- do_refresh ();
- if (s_stat->st_mtime > d_stat->st_mtime)
- return FILE_CONT;
- else
- return FILE_SKIP;
-
- case REPLACE_APPEND:
- do_append = 1;
- case REPLACE_YES:
- case REPLACE_ALWAYS:
- do_refresh ();
- return FILE_CONT;
- case REPLACE_NO:
- case REPLACE_NEVER:
- do_refresh ();
- return FILE_SKIP;
- case REPLACE_ABORT:
- default:
- return FILE_ABORT;
- }
- }
-
- int query_recursive (char *s)
- {
- char *confirm, *text;
-
- if (recursive_result < RECURSIVE_ALWAYS){
- text = copy_strings (" Delete: ", name_trunc (s, 30), " ", 0);
-
- if (know_what_am_i_doing){
- query_set_sel (1);
- recursive_result = query_dialog (text, "\n Directory not empty. "
- "\n Delete it recursively? ",
- 3 | WITH_HOTKEYS, 5,
- "y Yes ", "n No ", "l alL ", "e nonE ", "a Abort ");
- } else
- recursive_result = query_dialog (text, "\n Directory not empty. "
- "\n Delete it recursively? ",
- 3 | WITH_HOTKEYS, 5,
- "y Yes ", "n No ", "l alL ", "e nonE ", "a Abort ");
- if (recursive_result != RECURSIVE_ABORT)
- do_refresh ();
- free (text);
- if (!know_what_am_i_doing && (recursive_result == RECURSIVE_YES
- || recursive_result == RECURSIVE_ALWAYS)){
- text = copy_strings (" Please type 'yes' to confirm recursive delete of ",
- recursive_result == RECURSIVE_YES
- ? name_trunc (s, 20) : "all the directories", " ", 0);
- confirm = input_dialog (" Recursive Delete ",
- text, "no");
- do_refresh ();
- if (!confirm || strcmp (confirm, "yes"))
- recursive_result = RECURSIVE_NEVER;
- free (confirm);
- free (text);
- }
- }
- switch (recursive_result){
- case RECURSIVE_YES:
- case RECURSIVE_ALWAYS:
- return FILE_CONT;
- case RECURSIVE_NO:
- case RECURSIVE_NEVER:
- return FILE_SKIP;
- case RECURSIVE_ABORT:
- default:
- return FILE_ABORT;
- }
- }
-
- /* }}} */
-
- /* {{{ Copy routines */
-
- enum CaseConvs { NO_CONV=0, UP_CHAR=1, LOW_CHAR=2, UP_SECT=4, LOW_SECT=8 };
-
- int convert_case (int c, enum CaseConvs *conversion)
- {
- if (*conversion & UP_CHAR){
- *conversion &= ~UP_CHAR;
- return toupper (c);
- } else if (*conversion & LOW_CHAR){
- *conversion &= ~LOW_CHAR;
- return tolower (c);
- } else if (*conversion & UP_SECT){
- return toupper (c);
- } else if (*conversion & LOW_SECT){
- return tolower (c);
- } else
- return c;
- }
-
- static int transform_error = 0;
- static char *transform_source (char *source)
- {
- int j, k, l, len;
- char *fnsource = x_basename (source);
- int next_reg;
- enum CaseConvs case_conv = NO_CONV;
- static char fntarget [MC_MAXPATHLEN];
-
- len = strlen (fnsource);
- j = re_match (&rx, fnsource, len, 0, ®s);
- if (j != len) {
- transform_error = FILE_SKIP;
- return NULL;
- }
- for (next_reg = 1, j = 0, k = 0; j < strlen (dest_mask); j++) {
- switch (dest_mask [j]) {
- case '\\':
- j++;
- if (! isdigit (dest_mask [j])){
- /* Backslash followed by non-digit */
- switch (dest_mask [j]){
- case 'U':
- case_conv |= UP_SECT;
- case_conv &= ~LOW_SECT;
- break;
- case 'u':
- case_conv |= UP_CHAR;
- break;
- case 'L':
- case_conv |= LOW_SECT;
- case_conv &= ~UP_SECT;
- break;
- case 'l':
- case_conv |= LOW_CHAR;
- break;
- case 'E':
- case_conv = NO_CONV;
- break;
- default:
- /* Backslash as quote mark */
- fntarget [k++] = convert_case (dest_mask [j], &case_conv);
- }
- break;
- } else {
- /* Backslash followed by digit */
- next_reg = dest_mask [j] - '0';
- /* Fall through */
- }
-
- case '*':
- if (next_reg < 0 || next_reg >= RE_NREGS
- || regs.start [next_reg] < 0) {
- message (1, " Error ", " Invalid target mask ");
- transform_error = FILE_ABORT;
- return NULL;
- }
- for (l = regs.start [next_reg]; l < regs.end [next_reg]; l++)
- fntarget [k++] = convert_case (fnsource [l], &case_conv);
- next_reg ++;
- break;
-
- default:
- fntarget [k++] = convert_case (dest_mask [j], &case_conv);
- break;
- }
- }
- fntarget [k] = 0;
- return fntarget;
- }
-
- void free_linklist (void)
- {
- struct link *lp, *lp2;
-
- for (lp = linklist; lp != NULL; lp = lp2){
- lp2 = lp -> next;
- free (lp);
- }
- linklist = NULL;
- }
-
- /* Returns 0 if the inode wasn't found in the cache and 1 if it was found
- and a hardlink was succesfully made */
- int check_hardlinks (char *src_name, char *dst_name, struct stat *pstat)
- {
- struct link *lp;
- vfs *my_vfs = vfs_type (src_name);
- ino_t ino = pstat->st_ino;
- dev_t dev = pstat->st_dev;
- struct stat link_stat;
- char *p;
-
- if (vfs_file_is_ftp (src_name))
- return 0;
- for (lp = linklist; lp != NULL; lp = lp -> next)
- if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev){
- if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino &&
- link_stat.st_dev == dev && vfs_type (lp->name) == my_vfs){
- p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
- was copied to */
- if (vfs_type (dst_name) == vfs_type (p)){
- if (!mc_stat (p, &link_stat)){
- if (!mc_link (p, dst_name))
- return 1;
- }
- }
- }
- /* FIXME: Announce we couldn't make the hardlink */
- return 0;
- }
- lp = (struct link *) xmalloc (sizeof (struct link) + strlen (src_name)
- + strlen (dst_name) + 1, "Hardlink cache");
- if (lp){
- lp->vfs = my_vfs;
- lp->ino = ino;
- lp->dev = dev;
- strcpy (lp->name, src_name);
- p = strchr (lp->name, 0) + 1;
- strcpy (p, dst_name);
- lp->next = linklist;
- linklist = lp;
- }
- return 0;
- }
-
- int copy_file_file (char *src_path, char *dst_path, int toplevel,
- int ask_overwrite)
- {
- #ifndef _OS_NT
- uid_t src_uid;
- gid_t src_gid;
- #endif
- char *buf = 0;
- int buf_size = 8*1024;
- int dest_desc = 0;
- int source_desc;
- int n_read;
- int n_written;
- int src_mode; /* The mode of the source file */
- struct stat sb, sb2;
- struct utimbuf utb;
- int dst_exists = 0;
- long n_read_total = 0;
- long file_size;
- int return_status, temp_status;
- int do_remote_copy = 0;
- int appending = 0;
-
- return_status = FILE_RETRY;
-
- if (show_source (src_path) == FILE_ABORT
- || show_target (dst_path) == FILE_ABORT)
- return FILE_ABORT;
- refresh ();
-
- retry_dst_stat:
- if (!mc_lstat (dst_path, &sb2)){
- if (S_ISDIR (sb2.st_mode)){
- return_status = file_error (" Cannot overwrite directory \"%s\" ",
- dst_path);
- if (return_status == FILE_RETRY)
- goto retry_dst_stat;
- return return_status;
- }
- dst_exists = 1;
- }
-
- retry_src_lstat:
- if (mc_lstat (src_path, &sb)){
- return_status = file_error (" Cannot lstat source file \"%s\" \n %s ",
- src_path);
- if (return_status == FILE_RETRY)
- goto retry_src_lstat;
- return return_status;
- }
-
- if (dst_exists){
- /* Destination already exists */
- if (sb.st_dev == sb2.st_dev
- && sb.st_ino == sb2.st_ino){
- message (1, " Error ", " `%s' and `%s' are the same file. ",
- src_path, dst_path);
- do_refresh ();
- return FILE_SKIP;
- }
- if (S_ISDIR (sb2.st_mode)){
- message (1, " Error ", " Cannot overwrite directory `%s' ",
- dst_path);
- do_refresh ();
- return FILE_SKIP;
- }
-
- /* Should we replace destination? */
- if (ask_overwrite) {
- return_status = query_replace (dst_path, &sb, &sb2);
- if (return_status != FILE_CONT)
- return return_status;
- }
- }
-
- if (!do_append) {
-
- /* Check the hardlinks */
- if (sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb) == 1) {
- /* We have made a hardlink - no more processing is necessary */
- return return_status;
- }
- #ifndef _OS_NT
- if (S_ISLNK (sb.st_mode) && (!toplevel || !op_follow_symlinks)){
- char link_target [MC_MAXPATHLEN];
- int len;
-
- retry_src_readlink:
- len = mc_readlink (src_path, link_target, MC_MAXPATHLEN);
- if (len < 0){
- return_status = file_error
- (" Cannot read source link \"%s\" \n %s ", src_path);
- if (return_status == FILE_RETRY)
- goto retry_src_readlink;
- return return_status;
- }
- link_target [len] = 0;
- if (stable_symlinks && *link_target != PATH_SEP) {
- char *p, *q, *r, *s;
-
- p = strdup (src_path);
- r = strrchr (p, PATH_SEP);
- if (r) {
- r[1] = 0;
- if (*dst_path == PATH_SEP)
- q = strdup (dst_path);
- else
- q = copy_strings (p, dst_path, 0);
- r = strrchr (q, PATH_SEP);
- if (r) {
- r[1] = 0;
- s = copy_strings (p, link_target, NULL);
- strcpy (link_target, s);
- free (s);
- s = diff_two_paths (q, link_target);
- if (s) {
- strcpy (link_target, s);
- free (s);
- }
- }
- free (q);
- }
- free (p);
- }
-
- retry_dst_symlink:
- if (mc_symlink (link_target, dst_path) == 0)
- /* Success */
- return FILE_CONT;
- /*
- * if dst_exists, it is obvious that this had failed.
- * We can delete the old symlink and try again...
- */
- if (dst_exists && S_ISLNK (sb2.st_mode)) {
- if (!mc_unlink (dst_path))
- if (mc_symlink (link_target, dst_path) == 0)
- /* Success */
- return FILE_CONT;
- }
- return_status = file_error
- (" Cannot create target symlink \"%s\" \n %s ", dst_path);
- if (return_status == FILE_RETRY)
- goto retry_dst_symlink;
- return return_status;
- }
- #endif /* !OS_NT */
- if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) || S_ISFIFO (sb.st_mode)
- || S_ISSOCK (sb.st_mode)){
- retry_mknod:
- if (mc_mknod (dst_path, sb.st_mode, sb.st_rdev) < 0){
- return_status = file_error
- (" Cannot create special file \"%s\" \n %s ", dst_path);
- if (return_status == FILE_RETRY)
- goto retry_mknod;
- return return_status;
- }
- /* Success */
- return FILE_CONT;
- }
- }
-
- if (!do_append && !vfs_file_is_local (src_path) && vfs_file_is_local (dst_path)){
-
- mc_setctl (src_path, MCCTL_SETREMOTECOPY, dst_path);
- }
- retry_src_open:
- if ((source_desc = mc_open (src_path, O_RDONLY)) < 0){
- return_status = file_error
- (" Cannot open source file \"%s\" \n %s ", src_path);
- if (return_status == FILE_RETRY)
- goto retry_src_open;
- do_append = 0;
- return return_status;
- }
- #define USE_VFS_CTLS 1
- #ifdef USE_VFS_CTLS
- /* Before we can use the mc_ctl routine, the ftpfs code should be
- * changed so that it aborts connections as it should
- *
- * Jakub?
- */
- do_remote_copy = mc_ctl (source_desc, MCCTL_ISREMOTECOPY, 0);
- #else
- #define do_remote_copy 0
- #endif
-
- if (!do_remote_copy) {
- retry_src_fstat:
- if (mc_fstat (source_desc, &sb)){
- return_status = file_error
- (" Cannot fstat source file \"%s\" \n %s ", src_path);
- if (return_status == FILE_RETRY)
- goto retry_src_fstat;
- do_append = 0;
- goto ret;
- }
- } else {
- retry_src_rstat:
- if (mc_stat (src_path, &sb)){
- return_status = file_error
- (" Cannot stat source file \"%s\" \n %s ", src_path);
- if (return_status == FILE_RETRY)
- goto retry_src_rstat;
- do_append = 0;
- goto ret;
- }
- }
- src_mode = sb.st_mode;
- #ifndef _OS_NT
- src_uid = sb.st_uid;
- src_gid = sb.st_gid;
- utb.actime = sb.st_atime;
- utb.modtime = sb.st_mtime;
- #endif
- file_size = sb.st_size;
-
- /* If the target is a symlink and we do not want to append, we should
- unlink it first... */
-
- if (dst_exists && !do_append && S_ISLNK (sb2.st_mode))
- mc_unlink (dst_path);
-
- /* Create the new regular file with small permissions initially,
- to not create a security hole. */
-
- if (!do_remote_copy) {
- retry_dst_open:
- if ((do_append &&
- (dest_desc = mc_open (dst_path, O_WRONLY | O_APPEND)) < 0) ||
- (!do_append &&
- (dest_desc = mc_open (dst_path, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)) {
- return_status = file_error
- (" Cannot create target file \"%s\" \n %s ", dst_path);
- if (return_status == FILE_RETRY)
- goto retry_dst_open;
- do_append = 0;
- goto ret2;
- }
- }
- appending = do_append;
- do_append = 0;
-
- if (!do_remote_copy) {
- retry_dst_fstat:
- /* Find out the optimal buffer size. */
- if (mc_fstat (dest_desc, &sb)){
- return_status = file_error
- (" Cannot fstat target file \"%s\" \n %s ", dst_path);
- if (return_status == FILE_RETRY)
- goto retry_dst_fstat;
- goto ret2;
- }
- buf_size = 8*1024;
-
- buf = (char *) xmalloc (buf_size, "copy_file_file");
- }
-
- return_status = show_file_progress (0, file_size);
- refresh ();
- if (return_status != FILE_CONT)
- goto ret3;
-
- if (!do_remote_copy)
- for (;;){
- retry_src_read:
- n_read = mc_read (source_desc, buf, buf_size);
- if (n_read < 0){
- return_status = file_error
- (" Cannot read source file \"%s\" \n %s ", src_path);
- if (return_status == FILE_RETRY)
- goto retry_src_read;
- goto ret3;
- }
- if (n_read == 0)
- break;
-
- n_read_total += n_read;
-
- retry_dst_write:
- n_written = mc_write (dest_desc, buf, n_read);
- if (n_written < n_read){
- return_status = file_error
- (" Cannot write target file \"%s\" \n %s ", dst_path);
- if (return_status == FILE_RETRY)
- goto retry_dst_write;
- goto ret3;
- }
- return_status = show_file_progress (n_read_total, file_size);
- refresh ();
- if (return_status != FILE_CONT)
- goto ret3;
- }
- else {
- int i, size;
-
- for (i = 1; i;) {
- switch (size = mc_ctl (source_desc, MCCTL_REMOTECOPYCHUNK, 8192)) {
- case MCERR_TARGETOPEN:
- break;
- case MCERR_READ:
- break;
- case MCERR_WRITE:
- break;
- case MCERR_DATA_ON_STDIN:
- break;
- case MCERR_FINISH:
- i = 0;
- break;
- }
- if (size == MCERR_TARGETOPEN){
- message (1, " Error ", " Can't open target file ");
- goto ret3;
- }
- if (size == MCERR_READ)
- goto ret3;
-
- if (i && size != MCERR_DATA_ON_STDIN){
- n_read_total += size;
-
- /* Windows NT ftp servers report that files have no
- * permissions: -------, so if we happen to have actually
- * read something, we should fix the permissions.
- */
- if (!(src_mode &
- ((S_IRUSR|S_IWUSR|S_IXUSR) /* user */
- |(S_IXOTH|S_IWOTH|S_IROTH) /* other */
- |(S_IXGRP|S_IWGRP|S_IRGRP)))) /* group */
- src_mode = S_IRUSR|S_IWUSR|S_IROTH|S_IRGRP;
- }
- return_status = show_file_progress (n_read_total, file_size);
- refresh ();
- if (return_status != FILE_CONT)
- goto ret3;
- }
- }
-
- retry_dst_chmod:
- if (!appending && mc_chmod (dst_path, src_mode)){
- temp_status = file_error
- (" Cannot chmod target file \"%s\" \n %s ", dst_path);
- if (temp_status == FILE_RETRY)
- goto retry_dst_chmod;
- return_status = temp_status;
- }
- #ifndef _OS_NT
- if (!appending && preserve_uidgid) {
- retry_dst_chown:
- if (mc_chown (dst_path, src_uid, src_gid)){
- temp_status = file_error
- (" Cannot chown target file \"%s\" \n %s ", dst_path);
- if (temp_status == FILE_RETRY)
- goto retry_dst_chown;
- return_status = temp_status;
- }
- }
- #endif
- ret:
- if (buf)
- free (buf);
- ret0:
- retry_dst_close:
- if (!do_remote_copy) {
- if (mc_close (dest_desc) < 0){
- temp_status = file_error
- (" Cannot close target file \"%s\" \n %s ", dst_path);
- if (temp_status == FILE_RETRY)
- goto retry_dst_close;
- return_status = temp_status;
- }
- }
- if (!appending)
- utime (dst_path, &utb);
-
- ret2:
- retry_src_close:
- if (mc_close (source_desc) < 0){
- temp_status = file_error
- (" Cannot close source file \"%s\" \n %s ", src_path);
- if (temp_status == FILE_RETRY)
- goto retry_src_close;
- if (temp_status == FILE_ABORT)
- return_status = temp_status;
- }
-
- return return_status;
-
- ret3:
- /* Remove short file */
- mc_unlink (dst_path);
- if (do_remote_copy) {
- mc_ctl (source_desc, MCCTL_FINISHREMOTE, -1);
- goto ret0;
- }
- goto ret;
- }
-
- /*
- * I think these copy_*_* functions should have a return type.
- * anyway, this function *must* have two directories as arguments.
- */
- /* FIXME: This function needs to check the return values of the
- function calls */
- int copy_dir_dir (char *s, char *d, int toplevel, int move_over)
- {
- struct dirent *next;
- struct stat buf, cbuf;
- DIR *reading;
- char *path, *mdpath, *dest_file, *dest_dir;
- int return_status = FILE_CONT;
-
- /* First get the mode of the source dir */
- retry_src_stat:
- if (mc_lstat (s, &cbuf)){
- return_status = file_error (" Cannot stat source directory \"%s\" \n %s ", s);
- if (return_status == FILE_RETRY)
- goto retry_src_stat;
- return return_status;
- }
-
- /* FIXME: In this step we should do something
- in case the destination already exist */
- /* Check the hardlinks */
- if (cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf) == 1) {
- /* We have made a hardlink - no more processing is necessary */
- return return_status;
- }
-
- /* Can someone answer: can cbuf be a link? How? */
- if (S_ISLNK (cbuf.st_mode) && (!toplevel && dive_into_subdirs)){
- /* We want to replicate the symlink, not to copy the directory :-) */
- int result;
-
- if (!mc_stat (d, &buf) && toplevel && dive_into_subdirs){
- dest_dir = copy_strings (d, PATH_SEP_STR, x_basename (s), 0);
- result = copy_file_file (s, dest_dir, 1, 1);
- free (dest_dir);
- return result;
- } else
- return copy_file_file (s, d, 0, 1);
- }
- if (!S_ISDIR (cbuf.st_mode)){
- return_status = file_error (" Source directory \"%s\" is not a directory ", s);
- if (return_status == FILE_RETRY)
- goto retry_src_stat;
- return return_status;
- }
-
- /* Now, check if the dest dir exists, if not, create it. */
- if (mc_stat (d, &buf)){
- /* Here the dir doesn't exist : make it !*/
-
- if (move_over) {
- if (mc_rename (s, d) == 0)
- return FILE_CONT;
- }
- dest_dir = copy_strings (d, 0);
- } else {
- /*
- * If the destination directory exists, we want to copy the whole
- * directory, but we only want this to happen once.
- *
- * Escape sequences added to the * to avoid compiler warnings.
- * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
- * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
- */
- /* Again, I'm getting curious. Is not d already what we wanted, incl.
- masked source basename? Is not this just a relict of the past versions?
- I'm afraid this will lead into a two level deep dive :( */
- if (toplevel && dive_into_subdirs){
- dest_dir = copy_strings (d, PATH_SEP_STR, x_basename (s), 0);
- } else {
- dest_dir = copy_strings (d, 0);
- goto dont_mkdir;
- }
- }
- retry_dst_mkdir:
- if (my_mkdir (dest_dir, cbuf.st_mode | S_IRWXU)){
- return_status = file_error (" Cannot create target directory \"%s\" \n %s ", dest_dir);
- if (return_status == FILE_RETRY)
- goto retry_dst_mkdir;
- return return_status;
- }
-
- dont_mkdir:
- /* open the source dir for reading */
- reading = mc_opendir (s);
-
- if (!reading){
- free (dest_dir);
- return return_status;
- }
-
- while ((next = mc_readdir (reading)) && return_status != FILE_ABORT){
- /*
- * Now, we don't want '.' and '..' to be created / copied at any time
- */
- if (!strcmp (next->d_name, "."))
- continue;
- if (!strcmp (next->d_name, ".."))
- continue;
-
- /* get the filename and add it to the src directory */
- path = copy_strings (s, PATH_SEP_STR, next->d_name, 0);
- /* lstat is needed here, because we want even links to directories
- be replicated as links */
- mc_lstat (path, &buf);
-
- if (S_ISDIR (buf.st_mode)){
- mdpath = copy_strings (dest_dir, PATH_SEP_STR, next->d_name, 0);
- /*
- * From here, we just intend to recursively copy subdirs, not
- * the double functionality of copying different when the target
- * dir already exists. So, we give the recursive call the flag 0
- * meaning no toplevel.
- */
- return_status = copy_dir_dir (path, mdpath, 0, 0);
- free (mdpath);
- } else {
- dest_file = copy_strings (dest_dir, PATH_SEP_STR, x_basename (path), 0);
- return_status = copy_file_file (path, dest_file, 0, 1);
- free (dest_file);
- }
- free (path);
- }
- mc_closedir (reading);
- chmod (dest_dir, cbuf.st_mode);
- free (dest_dir);
- return return_status;
- }
-
- /* }}} */
-
- /* {{{ Move routines */
-
- static int move_file_file (char *s, char *d)
- {
- struct stat src_stats, dst_stats;
- int return_status = FILE_CONT;
-
- if (show_source (s) == FILE_ABORT
- || show_target (d) == FILE_ABORT)
- return FILE_ABORT;
-
- refresh ();
-
- retry_src_lstat:
- if (mc_lstat (s, &src_stats) != 0){
- /* Source doesn't exist */
- return_status = file_error (" Cannot stat file \"%s\" \n %s ", s);
- if (return_status == FILE_RETRY)
- goto retry_src_lstat;
- return return_status;
- }
-
- if (mc_lstat (d, &dst_stats) == 0){
- /* Destination already exists */
- if (src_stats.st_dev == dst_stats.st_dev
- && src_stats.st_ino == dst_stats.st_ino){
- int msize = COLS - 36;
-
- if (msize < 0)
- msize = 40;
- msize /= 2;
-
- message (1, " Error ", " `%s' and `%s' are the same file ",
- name_trunc (s, msize), name_trunc (d, msize) );
- do_refresh ();
- return FILE_SKIP;
- }
- if (S_ISDIR (dst_stats.st_mode)){
- message (1, " Error ", " Cannot overwrite directory `%s' ", d);
- do_refresh ();
- return FILE_SKIP;
- }
-
- if (confirm_overwrite){
- return_status = query_replace (d, &src_stats, &dst_stats);
- if (return_status != FILE_CONT)
- return return_status;
- }
- /* Ok to overwrite */
- }
-
- retry_rename:
- if (!do_append) {
- if (mc_rename (s, d) == 0)
- return FILE_CONT;
- }
- #if 0
- /* Comparison to EXDEV seems not to work in nfs if you're moving from
- one nfs to the same, but on the server it is on two different
- filesystems. Then nfs returns EIO instead of EXDEV.
- Hope it will not hurt if we always in case of error try to copy/delete. */
- else
- errno = EXDEV; /* Hack to copy (append) the file and then delete it */
-
- if (errno != EXDEV){
- return_status = files_error (" Cannot move file \"%s\" to \"%s\" \n %s ", s, d);
- if (return_status == FILE_RETRY)
- goto retry_rename;
- return return_status;
- }
- #endif
-
- /* Failed because filesystem boundary -> copy the file instead */
- if ((return_status = copy_file_file (s, d, 0, 0)) != FILE_CONT)
- return return_status;
- if ((return_status = show_source (NULL)) != FILE_CONT
- || (return_status = show_file_progress (0, 0)) != FILE_CONT)
- return return_status;
-
- refresh ();
-
- retry_src_remove:
- if (mc_unlink (s)){
- return_status = file_error (" Cannot remove file \"%s\" \n %s ", s);
- if (return_status == FILE_RETRY)
- goto retry_src_remove;
- return return_status;
- }
-
- return FILE_CONT;
- }
-
- int move_dir_dir (char *s, char *d)
- {
- struct stat sbuf, dbuf, destbuf;
- char *destdir;
- int return_status;
- int move_over = 0;
-
- if (show_source (s) == FILE_ABORT
- || show_target (d) == FILE_ABORT)
- return FILE_ABORT;
-
- refresh ();
-
- mc_stat (s, &sbuf);
- if (mc_stat (d, &dbuf))
- destdir = copy_strings (d, 0);
- else if (!dive_into_subdirs) {
- destdir = copy_strings (d, 0);
- move_over = 1;
- } else
- /* And I'm not sure about this as well - shouldn't we strdup always? */
- destdir = copy_strings (d, PATH_SEP_STR, x_basename (s), 0);
-
- /* Check if the user inputted an existing dir */
- retry_dst_stat:
- if (!mc_stat (destdir, &destbuf)){
- if (move_over) {
- if ((return_status = copy_dir_dir (s, destdir, 0, 1)) != FILE_CONT)
- goto ret;
- goto oktoret;
- } else {
- if (S_ISDIR (destbuf.st_mode))
- return_status = file_error (" Cannot overwrite directory \"%s\" ", destdir);
- else
- return_status = file_error (" Cannot overwrite file \"%s\" ", destdir);
- if (return_status == FILE_RETRY)
- goto retry_dst_stat;
- }
- free (destdir);
- return return_status;
- }
-
- retry_rename:
- if (mc_rename (s, destdir) == 0){
- return_status = FILE_CONT;
- goto ret;
- }
-
- if (errno != EXDEV){
- return_status = files_error (" Cannot move directory \"%s\" to \"%s\" \n %s ", s, d);
- if (return_status == FILE_RETRY)
- goto retry_rename;
- goto ret;
- }
-
- /* Failed because of filesystem boundary -> copy dir instead */
- if ((return_status = copy_dir_dir (s, destdir, 0, 0)) != FILE_CONT)
- goto ret;
- oktoret:
- if ((return_status = show_source (NULL)) != FILE_CONT
- || (return_status = show_file_progress (0, 0)) != FILE_CONT)
- goto ret;
-
- refresh ();
-
- return_status = recursive_erase (s);
-
- ret:
- free (destdir);
- return return_status;
- }
-
- /* }}} */
-
- /* {{{ Erase routines */
-
- static int erase_file (char *s)
- {
- int return_status;
-
- if (show_deleting (s) == FILE_ABORT)
- return FILE_ABORT;
-
- refresh ();
-
- retry_unlink:
- if (mc_unlink (s)){
- return_status = file_error (" Cannot delete file \"%s\" \n %s ", s);
- if (return_status == FILE_RETRY)
- goto retry_unlink;
- return return_status;
- }
- return FILE_CONT;
- }
-
- static int recursive_erase (char *s)
- {
- struct dirent *next;
- struct stat buf;
- DIR *reading;
- char *path;
- int return_status = FILE_CONT;
-
- if (!strcmp (s, ".."))
- return 1;
-
- reading = mc_opendir (s);
-
- if (!reading)
- return 1;
-
- while ((next = mc_readdir (reading)) && return_status == FILE_CONT){
- if (!strcmp (next->d_name, "."))
- continue;
- if (!strcmp (next->d_name, ".."))
- continue;
- path = copy_strings (s, PATH_SEP_STR, next->d_name, 0);
- if (mc_lstat (path, &buf)){
- free (path);
- return 1;
- }
- if (S_ISDIR (buf.st_mode))
- return_status = (recursive_erase (path) != FILE_CONT);
- else
- return_status = erase_file (path);
- free (path);
- }
- mc_closedir (reading);
- if (return_status != FILE_CONT)
- return return_status;
- if (show_deleting (s) == FILE_ABORT)
- return FILE_ABORT;
- refresh ();
- retry_rmdir:
- if (my_rmdir (s)){
- return_status = file_error (" Cannot remove directory \"%s\" \n %s ", s);
- if (return_status == FILE_RETRY)
- goto retry_rmdir;
- return return_status;
- }
- return FILE_CONT;
- }
-
- int erase_dir (char *s)
- {
- int error;
-
- if (strcmp (s, "..") == 0)
- return FILE_SKIP;
-
- if (strcmp (s, ".") == 0)
- return FILE_SKIP;
-
- if (show_deleting (s) == FILE_ABORT)
- return FILE_ABORT;
- refresh ();
-
- retry_rmdir:
- error = my_rmdir (s);
- if (error && (errno == ENOTEMPTY || errno == EEXIST)){
- error = query_recursive (s);
- if (error == FILE_CONT)
- return recursive_erase (s);
- else
- return error;
- }
- if (error){
- error = file_error (" Cannot remove directory \"%s\" \n %s ", s);
- if (error == FILE_RETRY)
- goto retry_rmdir;
- return error;
- }
- return FILE_CONT;
- }
-
- /* }}} */
-
- /* {{{ Panel operate routines */
-
- /* Returns currently selected file or the first marked file if there is one */
- static char *get_file (struct stat *stat_buf)
- {
- int i;
- WPanel *panel;
-
- if (get_current_type () == view_tree){
- WTree *tree = (WTree *)get_panel_widget (get_current_index ());
-
- mc_stat (tree->selected_ptr->name, stat_buf);
- return tree->selected_ptr->name;
- }
-
- panel = cpanel;
-
- if (panel->marked){
- for (i = 0; i < panel->count; i++)
- if (panel->dir.list [i].f.marked){
- *stat_buf = panel->dir.list [i].buf;
- return panel->dir.list [i].fname;
- }
- } else {
- *stat_buf = panel->dir.list [panel->selected].buf;
- return panel->dir.list [panel->selected].fname;
- }
- fprintf (stderr, " Internal error: get_file \n");
- mi_getch ();
- return "";
- }
-
- static int is_wildcarded (char *p)
- {
- for (; *p; p++) {
- if (*p == '*')
- return 1;
- else if (*p == '\\' && p [1] >= '1' && p [1] <= '9')
- return 1;
- }
- return 0;
- }
-
- #if BACKGROUND
-
- int attention = 0;
- int we_are_background = 0;
-
- int
- background_attention (int fd)
- {
- char c;
-
- read (fd, &c, 1);
- attention = 1;
- }
-
- int background_processor (int fd)
- {
- int c;
- char buffer [1024];
-
- while ((c = read (fd, buffer, sizeof (buffer))) > 0){
- if (attention)
- write (1, buffer, c);
- }
- }
-
- void
- set_nonblocking (int fd)
- {
- int val;
-
- val = fcntl (fd, F_GETFL, 0);
- val |= O_NONBLOCK;
- fcntl (fd, F_SETFL, val);
- }
-
- /* Try to make the Midnight Commander a background job */
- /* return -1 on failure */
- static int
- do_background ()
- {
- int fds [2]; /* stdin/stdout streams */
- int comm [2]; /* data connection stream */
- int pid;
-
- if (socketpair (AF_UNIX, SOCK_STREAM, 0, fd) == -1)
- return -1;
- if (socketpair (AF_UNIX, SOCK_STREAM, 0, comm) == -1){
- close (fd [0]);
- close (fd [1]);
- return -1;
- }
-
- if ((pid = fork ()) == -1)
- return -1;
-
- if (pid == 0){
- close (fd [0]);
- close (comm [0]);
- dup2 (fd [1], 0); /* stdin */
- dup2 (fd [1], 1); /* stdout */
- dup2 (fd [1], 2); /* stderr */
- we_are_background = 1;
- } else {
- close (fd [1]);
- close (comm [1]);
- add_select_channel (fd [0], background_processor, 0);
- set_non_blocking (fd [0]);
- add_select_channel (comm [0], background_attention, 0);
- }
- }
- #endif
-
- char *file_mask_dialog (char *header, char *text, char *def_text, int only_one)
- {
- #define FMDY 13
- int source_easy_patterns = easy_patterns;
- char *source_mask, *orig_mask, *dest_dir;
- const char *error;
- struct stat buf;
- int notroot, val;
-
- QuickDialog Quick_input;
- QuickWidget quick_widgets [] = {
- /* preserve UID/GID must be the first one */
- { quick_checkbox, 3, 64, 8, FMDY, "preserve UIDs/GIDs", 'U', 0, 0,
- &preserve_uidgid, 0, XV_WLAY_BELOWCLOSE, "t." },
- { quick_checkbox, 42, 64, 8, FMDY, "Stable Symlinks", 'S', 0, 0,
- &stable_symlinks, 0, XV_WLAY_BELOWCLOSE, "" },
- { quick_checkbox, 23, 64, 7, FMDY, "Dive into subdir if exists", 'D', 0, 0,
- &dive_into_subdirs, 0, XV_WLAY_RIGHTOF, "" },
- { quick_checkbox, 3, 64, 7, FMDY, "follow sYmlinks", 'y', 0, 0,
- &op_follow_symlinks, 0, XV_WLAY_BELOWOF, "" },
- #ifdef HAVE_XVIEW
- #define FMDI1 7
- #define FMDI2 4
- #define FMDC 6
- { quick_input, 3, 64, 6, FMDY, 0, 0, 58, 0,
- 0, 0, XV_WLAY_BELOWCLOSE, "" },
- #endif
- { quick_label, 3, 64, 5, FMDY, "to:", 0, 0, 0, 0, 0, XV_WLAY_BELOWOF,"d."},
- { quick_checkbox, 37, 64, 4, FMDY, "using shell Patterns", 'p', 0, 0,
- 0/* &source_easy_patterns */, 0, XV_WLAY_BELOWCLOSE, "s." },
- { quick_input, 3, 64, 3, FMDY, 0 /* default text */, 0, 58,
- 0, 0, 0, XV_WLAY_BELOWCLOSE, "" },
- #ifndef HAVE_XVIEW
- #define FMDI1 6
- #define FMDI2 7
- #define FMDC 5
- { quick_input, 3, 64, 6, FMDY, 0, 0, 58, 0,
- 0, 0, XV_WLAY_BELOWCLOSE },
- #endif
- #define FMDI0 8
- { quick_label, 2, 64, 2, FMDY, 0, 0, 0, 0, 0, 0, XV_WLAY_DONTCARE },
- #define SKIP 2
- { quick_button, 34, 64, 9, FMDY, "[ Cancel ]", 'C', 2, B_CANCEL, 0, 0, 0,
- "b." },
- { quick_button, 20, 64, 9, FMDY, "[ Ok ]", 'O', 2, B_ENTER, 0, 0, 0, "" },
- #if 0
- { quick_button, 48, 64, 9, FMDY, "[ & ]", '&', 2, B_USER, 0, 0, 0, 0, "" },
- #endif
- { 0 } };
-
- stable_symlinks = 0;
- quick_widgets [FMDC].result = &source_easy_patterns;
- quick_widgets [FMDI1].text = easy_patterns ? "*" : "^\\(.*\\)$";
- notroot = (geteuid ()) ? 1 : 0;
- Quick_input.xlen = 64;
- Quick_input.xpos = -1;
- Quick_input.title = header;
- Quick_input.help = "[Mask Copy/Rename]";
- Quick_input.class = "quick_file_mask";
- quick_widgets [FMDI0].text = text;
- quick_widgets [FMDI2].text = def_text;
- quick_widgets [FMDI2].str_result = &dest_dir;
- quick_widgets [FMDI1].str_result = &source_mask;
- Quick_input.ylen = FMDY;
- Quick_input.widgets = quick_widgets + notroot;
- if (notroot) {
- int i;
-
- preserve_uidgid = 0;
- quick_widgets [1].tk_frame = "t.";
- }
-
- ask_file_mask:
-
- if ((val = quick_dialog_skip (&Quick_input, SKIP)) == B_CANCEL)
- return 0;
-
- #if BACKGROUND
- /* If it is a background operation */
- if (val == B_USER){
- if (do_background () != -1)
- return 0;
- /* If it did fail, continue as if no background support */
- }
- #endif
-
- orig_mask = source_mask;
- if (!dest_dir || !*dest_dir) {
- if (source_mask)
- free (source_mask);
- return dest_dir;
- }
- if (source_easy_patterns) {
- source_easy_patterns = easy_patterns;
- easy_patterns = 1;
- source_mask = convert_pattern (source_mask, match_file, 1);
- source_easy_patterns = easy_patterns;
- }
- error = re_compile_pattern (source_mask, strlen (source_mask), &rx);
- if (error) {
- message (1, " Error ", "Invalid source pattern \"%s\" \n %s ",
- orig_mask, error);
- if (orig_mask)
- free (orig_mask);
- goto ask_file_mask;
- }
- if (orig_mask)
- free (orig_mask);
- dest_mask = strrchr (dest_dir, PATH_SEP);
- if (dest_mask == NULL)
- dest_mask = dest_dir;
- else
- dest_mask++;
- orig_mask = dest_mask;
- if (!*dest_mask || (!dive_into_subdirs && !is_wildcarded (dest_mask)) ||
- (dive_into_subdirs && ((!only_one && !is_wildcarded (dest_mask)) ||
- (only_one && !mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode)))))
- dest_mask = strdup ("*");
- else {
- dest_mask = strdup (dest_mask);
- *orig_mask = 0;
- }
- if (!*dest_dir) {
- free (dest_dir);
- dest_dir = strdup ("./");
- }
- return dest_dir;
- }
-
- /* Returns 1 if did change the directory structure,
- Returns 0 if user aborted */
- int panel_operate (int operation, char *thedefault)
- {
- #ifdef WITH_FULL_PATHS
- char *source_with_path = NULL;
- #else
- # define source_with_path source
- #endif
- char *source = NULL;
- char *dest = NULL;
- char *temp = NULL;
- int only_one = (get_current_type () == view_tree) || (cpanel->marked <= 1);
- struct stat src_stat, dst_stat;
- int i, value;
- long marked, total;
- long count = 0, bytes = 0;
- int dst_result;
-
- rx.buffer = NULL;
- if (linklist)
- free_linklist ();
- if (get_current_type () == view_listing)
- if (!cpanel->marked && !strcmp (selection (cpanel)->fname, "..")){
- message (1, " Error ", " Can't operate on \"..\"! ");
- return 0;
- }
-
- if (operation < OP_COPY || operation > OP_DELETE)
- return 0;
-
- /* Generate confirmation prompt */
-
- if (!only_one){
- sprintf (cmd_buf, "%s%d %s%s ", op_names [operation], cpanel->marked,
- (cpanel->marked == cpanel->dirs_marked) ? "directories" :
- (cpanel->dirs_marked) ? "files/directories" : "files",
- (operation == OP_DELETE) ? "?" : " with source mask:");
- } else {
- source = get_file (&src_stat);
- sprintf (cmd_buf,"%s%s \"%s\"%s ", op_names [operation],
- S_ISDIR (src_stat.st_mode) ? "directory" : "file",
- name_trunc (source, 28),
- (operation == OP_DELETE) ? "?" : " with source mask:");
- }
-
- /* Show confirmation dialog */
- if (operation == OP_DELETE && confirm_delete){
- i = query_dialog (op_names [operation], cmd_buf,
- 3, 2, " Yes ", " No ");
- if (i != 0)
- return 0;
- } else if (operation != OP_DELETE) {
- char *dest_dir;
-
- if (thedefault != NULL)
- dest_dir = thedefault;
- else if (get_other_type () == view_listing)
- dest_dir = opanel->cwd;
- else
- dest_dir = cpanel->cwd;
-
- rx.buffer = (char *) xmalloc (MC_MAXPATHLEN, "mask copying");
- rx.allocated = MC_MAXPATHLEN;
- rx.translate = 0;
- dest = file_mask_dialog (op_names [operation],
- cmd_buf, dest_dir, only_one);
- if (!dest) {
- free (rx.buffer);
- return 0;
- }
- if (!*dest){
- free (rx.buffer);
- free (dest);
- return 0;
- }
- }
-
- /* Initialize things */
- create_op_win (operation);
- ftpfs_hint_reread (0);
-
- /* Now, let's do the job */
- /* This code is only called by the tree and panel code */
- if (only_one){
- /* One file: FIXME mc_chdir will take user out of any vfs */
- if (operation != OP_COPY && get_current_type () == view_tree)
- mc_chdir (PATH_SEP_STR);
-
- /* The source and src_stat variables have been initialized before */
- #ifdef WITH_FULL_PATHS
- source_with_path = copy_strings (cpanel->cwd, PATH_SEP_STR, source, 0);
- #endif
-
- if (operation == OP_DELETE){
- /* Delete operation */
- if (S_ISDIR (src_stat.st_mode))
- value = erase_dir (source_with_path);
- else
- value = erase_file (source_with_path);
- } else {
- /* Copy or move operation */
- temp = transform_source (source_with_path);
- if (temp == NULL) {
- value = transform_error;
- } else {
- temp = get_full_name (dest, temp);
- free (dest);
- dest = temp;
- temp = 0;
- switch (operation){
- case OP_COPY:
- if (S_ISDIR (src_stat.st_mode))
- value = copy_dir_dir (source_with_path, dest, 1, 0);
- else
- value = copy_file_file (source_with_path, dest, 1, 1);
- break;
- case OP_MOVE:
- if (S_ISDIR (src_stat.st_mode))
- value = move_dir_dir (source_with_path, dest);
- else
- value = move_file_file (source_with_path, dest);
- break;
- default:
- value = FILE_CONT;
- message (1, " Internal failure ", " Unknown file operation ");
- }
- }
- } /* Copy or move operation */
-
- if (value == FILE_CONT)
- unmark_files (cpanel);
-
- } else {
- /* Many files */
-
- if (operation != OP_DELETE){
- /* Check destination for copy or move operation */
- retry_many_dst_stat:
- dst_result = mc_stat (dest, &dst_stat);
- if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)){
- if (file_error (" Destination \"%s\" must be a directory ", dest) == FILE_RETRY)
- goto retry_many_dst_stat;
- goto clean_up;
- }
- }
-
- /* Initialize variables for progress bars */
- marked = cpanel->marked;
- total = cpanel->total;
-
- /* Loop for every file */
- for (i = 0; i < cpanel->count; i++){
- if (!cpanel->dir.list [i].f.marked)
- continue; /* Skip the unmarked ones */
- source = cpanel->dir.list [i].fname;
- src_stat = cpanel->dir.list [i].buf; /* Inefficient, should we use pointers? */
- #ifdef WITH_FULL_PATHS
- if (source_with_path)
- free (source_with_path);
- source_with_path = copy_strings (cpanel->cwd, PATH_SEP_STR, source, 0);
- #endif
- if (operation == OP_DELETE){
- /* Delete operation */
- if (S_ISDIR (src_stat.st_mode))
- value = erase_dir (source_with_path);
- else
- value = erase_file (source_with_path);
- } else {
- /* Copy or move operation */
- if (temp)
- free (temp);
- temp = transform_source (source_with_path);
- if (temp == NULL) {
- value = transform_error;
- } else {
- temp = get_full_name (dest, temp);
- switch (operation){
- case OP_COPY:
- if (S_ISDIR (src_stat.st_mode))
- value = copy_dir_dir (source_with_path, temp, 1, 0);
- else
- value = copy_file_file (source_with_path, temp, 1, 1);
- break;
- case OP_MOVE:
- if (S_ISDIR (src_stat.st_mode))
- value = move_dir_dir (source_with_path, temp);
- else
- value = move_file_file (source_with_path, temp);
- break;
- default:
- message (1, " Internal failure ",
- " Unknown file operation ");
- goto clean_up;
- }
- }
- } /* Copy or move operation */
-
- if (value == FILE_ABORT)
- goto clean_up;
- if (value == FILE_CONT){
- file_mark (cpanel, i, 0);
- cpanel->marked --;
- if (S_ISDIR (src_stat.st_mode))
- cpanel->dirs_marked --;
- cpanel->total -= src_stat.st_size;
- }
- count ++;
- if (show_count_progress (count, marked) == FILE_ABORT)
- goto clean_up;
- bytes += src_stat.st_size;
- if (verbose &&
- show_bytes_progress (bytes, total) == FILE_ABORT)
- goto clean_up;
- if (operation != OP_DELETE && verbose
- && show_file_progress (0, 0) == FILE_ABORT)
- goto clean_up;
- refresh ();
- } /* Loop for every file */
- } /* Many files */
-
- clean_up:
- /* Clean up */
- destroy_op_win ();
- ftpfs_hint_reread (1);
- if (linklist)
- free_linklist ();
- #if WITH_FULL_PATHS
- if (source_with_path)
- free (source_with_path);
- #endif
- if (dest)
- free (dest);
- if (temp)
- free (temp);
- if (rx.buffer) {
- free (rx.buffer);
- rx.buffer = NULL;
- }
- if (dest_mask) {
- free (dest_mask);
- dest_mask = NULL;
- }
- return 1;
- }
-
- /* }}} */
-
- /*
- Cause emacs to enter folding mode for this file:
- Local variables:
- end:
- */
-